package bomberman.modelo;

import java.util.ArrayList;
import java.util.HashMap;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;

import javax.swing.JOptionPane;

import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.input.SAXBuilder;
import org.jdom2.output.Format;
import org.jdom2.output.XMLOutputter;

import fiuba.algo3.titiritero.modelo.ObjetoDibujable;
import fiuba.algo3.titiritero.modelo.ObjetoVivo;
import bomberman.modelo.mapa.Nivel;
import bomberman.modelo.mapa.Posicion;
import bomberman.modelo.mapa.Casillero;
import bomberman.modelo.mapa.Mapa;
import bomberman.modelo.mapa.Salida;
import bomberman.modelo.mapa.TipoBloque;
import bomberman.modelo.observadores.Observadores;
import bomberman.modelo.obstaculos.BloqueAcero;
import bomberman.modelo.obstaculos.BloqueCemento;
import bomberman.modelo.obstaculos.BloqueLadrillo;
import bomberman.modelo.personajes.Bombita;
import bomberman.modelo.personajes.Cecilio;
import bomberman.modelo.personajes.Enemigo;
import bomberman.modelo.personajes.LopezReggae;
import bomberman.modelo.personajes.LopezReggaeAlado;
import bomberman.modelo.personajes.TipoEnemigo;
import bomberman.vista.Dimensionador;

public class Partida {
  private static final Posicion posicionInicial = new Posicion(1, 1);
	
  private static final int VIDAS_RESTANTES_STD = 3;
  private static final int PUNTOS_PASAR_NIVEL  = 1000;
  private Nivel nivel;
  private int              nivelActual = 0;
  private int              puntos = 0;
  private int              vidasRestantes;
  private NuestroGameLoop         gameLoop;
  private HashMap<Object, ObjetoDibujable> traductor;
  private Observadores observadores;
  
  public Partida(NuestroGameLoop unGameLoop) {
	this.gameLoop = unGameLoop;
	traductor = new HashMap<Object, ObjetoDibujable>();
	observadores = new Observadores(this);
	
	Dimensionador.setFactorDeConversion(30);
	
	nivel = cargarNivel(nivelActual); 
	nivel.setBombita(new Bombita(nivel.getMapa().getCasillero(posicionInicial)), posicionInicial);
    
    this.vidasRestantes = Partida.VIDAS_RESTANTES_STD;
  }
  

  public Partida(NuestroGameLoop nuevoGameLoop, Element root) {
	this.gameLoop = nuevoGameLoop;
	traductor = new HashMap<Object, ObjetoDibujable>();
	observadores = new Observadores(this);
	Dimensionador.setFactorDeConversion(30);
	
	this.nivelActual=Integer.parseInt(root.getAttributeValue("NivelActual"));
	this.puntos= Integer.parseInt(root.getAttributeValue("Puntos"));
	this.vidasRestantes= Integer.parseInt(root.getAttributeValue("VidasRestantes"));
	this.nivel = new Nivel(root.getChild("Nivel"));
	
	nivel.getMapa().getSalida().setPartida(this);
}

  private Nivel cargarNivel(int numeroNivel) {	  
	SAXBuilder builder = new SAXBuilder();
	Document documento = null;
		
	try {
		documento = builder.build("niveles/" + numeroNivel + ".xml");
	} catch (JDOMException e) {
		e.printStackTrace();
	} catch (IOException e) {
		e.printStackTrace();
	}
	
	Element elementoRaiz = documento.getRootElement();
	
	// Creamos el mapa
	int tamanioMapa = Integer.parseInt(elementoRaiz.getAttributeValue("tamanio"));
	Mapa mapa = null;
	Nivel nivel = null;
	
	List<Element> nodos = elementoRaiz.getChildren();	
	for (Element nodo : nodos){
		if(nodo.getName().equals("salida")){
		  Element nodoSalida = (Element) nodo;
		  int x = Integer.parseInt(nodoSalida.getAttributeValue("x"));
		  int y = Integer.parseInt(nodoSalida.getAttributeValue("y"));
		  
		  Posicion posicionSalida = new Posicion(x, y);
		  mapa = new Mapa(tamanioMapa, posicionSalida);
		  mapa.getSalida().setPartida(this);
		  nivel = new Nivel(mapa);
		}else if(nodo.getName().equals("enemigos")){
		  List<Element> nodosEnemigos = nodo.getChildren();
		  
		  for (Element nodoEnemigo : nodosEnemigos) {
			  TipoEnemigo tipoEnemigo = TipoEnemigo.valueOf(nodoEnemigo.getAttributeValue("tipo"));
			  
			  int x = Integer.parseInt(nodoEnemigo.getAttributeValue("x"));
			  int y = Integer.parseInt(nodoEnemigo.getAttributeValue("y"));
			  
			  Posicion posicion = new Posicion(x, y);
			  Casillero casilleroABloquear = mapa.getCasillero(posicion);
			  Enemigo enemigo = null;
			  switch(tipoEnemigo) {
				case CECILIO:
					enemigo = new Cecilio(casilleroABloquear);
					break;
				case LOPEZREGGAE:
					enemigo = new LopezReggae(casilleroABloquear);
					break;
				case LOPEZREGGAEALADO:
					enemigo = new LopezReggaeAlado(casilleroABloquear);
			  }	
			  
			  nivel.agregarEnemigo(enemigo);
		  }
	  }else if(nodo.getName().equals("bloques")){
		  List<Element> nodosBloques = nodo.getChildren();
		  
		  for (Element nodoBloque : nodosBloques) {			  
			  TipoBloque tipoBloque = TipoBloque.valueOf(nodoBloque.getAttributeValue("tipo"));
			  
			  int x = Integer.parseInt(nodoBloque.getAttributeValue("x"));
			  int y = Integer.parseInt(nodoBloque.getAttributeValue("y"));
			  
			  Posicion posicion = new Posicion(x, y);
			  Casillero casilleroABloquear = mapa.getCasillero(posicion);
			  
			  switch(tipoBloque) {
				case LADRILLO:
					new BloqueLadrillo(casilleroABloquear);
					break;
				case CEMENTO:
					new BloqueCemento(casilleroABloquear);
					break;
				case ACERO:
					new BloqueAcero(casilleroABloquear);
			}	
		  }
	  }
	}
	
	return nivel;
  }
  
  public Nivel getNivelActual() {
    return nivel;
  }
  
  public int getNumeroDeNivelActual() {
    return this.nivelActual;
  }
  
  public void pasarDeNivel(Bombita unBombita) {
	// Si no estan todos los enemigos muertos, no hago nada
	if (!nivel.enemigosMuertos()) {
		return;
	}
	
	Salida salida = null;  
	ArrayList<Object> VistasAEliminar = new ArrayList<Object>();
	for(Object obj: traductor.keySet()){
		if(obj instanceof Salida){
			salida = (Salida) obj;
			break;
		}
		VistasAEliminar.add(obj);
	}
	this.retirarObjetoVivoYVista(salida);
	
	for(int i=0; i<VistasAEliminar.size(); i++){
		this.retirarVista(VistasAEliminar.get(i));
	}
	
	JOptionPane.showMessageDialog(null, "NIVEL SUPERADO");

	puntos += Partida.PUNTOS_PASAR_NIVEL * (this.nivelActual + 1);
        
    nivel = cargarNivel(++nivelActual);
    nivel.setBombita(unBombita, posicionInicial);
    
    Observadores.notificar(this);
  }
  
  public int getPuntos() {
    return this.puntos;
  }
  
  public void sumarPuntos(int puntos) {
    this.puntos += puntos;
  }
  
  public int getVidasRestantes() {
    return this.vidasRestantes;
  }
  
  public void perderVida() {
    this.vidasRestantes -= 1;
    if (this.vidasRestantes >= 0) {
    	Bombita bombita = nivel.getBombita();
    	bombita.revivir();
    	nivel.setBombita(bombita, posicionInicial);
    	Observadores.notificar(this);
    } else {
      gameLoop.detenerEjecucion();
	  
   	  JOptionPane.showMessageDialog(null, "PERDISTE");

    }
  }

  public NuestroGameLoop getGameLoop() {
    return gameLoop;
  }
  
  public Observadores getObservadores(){
	return observadores;
  }
  
  public HashMap<Object, ObjetoDibujable> getTraductor(){
	  return traductor;
  }
  
  public void setGameLoop(NuestroGameLoop gameLoop) {
    this.gameLoop = gameLoop;
  }

  public void agregarObjetoVivoYVista(Object objeto, ObjetoDibujable vista){
	  gameLoop.agregar((ObjetoVivo) objeto);
	  this.gameLoop.agregar(vista);
	  this.traductor.put(objeto, vista);
  }
  
  public void agregarObjetoVivo(Object objeto){
	  gameLoop.agregar((ObjetoVivo) objeto);
  }
  
  public void retirarObjetoVivo(Object objeto){
	  gameLoop.remover((ObjetoVivo) objeto);
  }
  
  public void agregarVista(Object objeto, ObjetoDibujable vista){
	  this.gameLoop.agregar(vista);
	  this.traductor.put(objeto, vista);
  }
   
  public void retirarObjetoVivoYVista(Object contenido){
    gameLoop.remover((ObjetoVivo) contenido);
    gameLoop.remover(traductor.get(contenido));
    traductor.remove(contenido);
  }
  
  public void retirarVista(Object contenido){
    gameLoop.remover(traductor.get(contenido));
	traductor.remove(contenido);
  }
  
  public void cambiarVista(Object objeto, ObjetoDibujable nuevaVista){
	  gameLoop.remover(traductor.get(objeto));
	  gameLoop.agregar(nuevaVista);
	  traductor.put(objeto, nuevaVista);
  }
  
  public void serializar(File archivoSalida) throws IOException {
	  XMLOutputter outputter = new XMLOutputter(Format.getPrettyFormat());
	  FileOutputStream archivoXML = new FileOutputStream(archivoSalida);
	  			  
	  Element ePartida= new Element("Partida");
	  ePartida.setAttribute("Puntos",String.valueOf(this.puntos));
	  ePartida.setAttribute("VidasRestantes",String.valueOf(this.vidasRestantes));
	  ePartida.setAttribute("NivelActual",String.valueOf(this.nivelActual));
	  ePartida.addContent(nivel.serializar());
	  
	  outputter.output(ePartida, archivoXML);
	  archivoXML.close();
  }
  

	public static Partida cargarPartidaDeXML(File archivoEntrada, NuestroGameLoop nuevoGameLoop) {
		// Se parsea el documento
    	SAXBuilder parser = new SAXBuilder();
    	Document archivoXML = null;
    	
    	try {
			archivoXML = parser.build(archivoEntrada);
		} catch (JDOMException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		Element root = archivoXML.getRootElement();
		
		return deserealizar(root,nuevoGameLoop);
	}
	
	private static Partida deserealizar(Element root, NuestroGameLoop nuevoGameLoop) {
		Partida partidaRecuperada= new Partida(nuevoGameLoop, root);
		
		return partidaRecuperada;
	}  
}

